추적 가비지 컬렉션
1. 개요
1. 개요
추적 가비지 컬렉션은 가비지 컬렉션의 주요 방식 중 하나로, 프로그래밍 언어의 런타임 시스템이 더 이상 사용되지 않는 동적 메모리 할당된 객체를 자동으로 찾아내고 회수하는 메모리 관리 기법이다. 이 기법은 메모리 누수를 방지하고 프로그래머가 수동으로 메모리를 해제하는 부담을 덜어주는 데 주로 사용된다.
이 방식의 핵심은 도달 가능성 분석에 기반한다. 가비지 컬렉터는 프로그램의 루트 셋(전역 변수, 스택 프레임의 지역 변수, 레지스터 등)으로부터 시작하여 참조를 따라갈 수 있는 모든 객체를 '도달 가능'한 것으로 표시한다. 이 과정을 마킹이라고 한다. 이후 마킹 단계에서 표시되지 않은 객체들은 프로그램이 더 이상 접근할 수 없는 '가비지'로 판단되어 메모리에서 해제된다. 이 해제 과정을 스위핑이라고 한다.
추적 가비지 컬렉션은 자바, C 샤프, 파이썬, 자바스크립트 등 많은 현대 고수준 프로그래밍 언어의 메모리 모델에서 표준으로 채택되어 있다. 이를 구현한 대표적인 가비지 컬렉터로는 JVM의 다양한 가비지 컬렉터와 .NET CLR의 가비지 컬렉터가 있다. 이 기법은 컴파일러와 런타임이 밀접하게 협력하여 동작한다.
2. 기본 원리
2. 기본 원리
2.1. 도달 가능성 분석
2.1. 도달 가능성 분석
추적 가비지 컬렉션의 핵심 첫 단계는 도달 가능성 분석이다. 이 과정은 프로그램에서 현재 사용 중인 객체와 더 이상 필요 없는 객체를 구분하는 기준을 설정한다. 분석은 프로그램 실행 중 특정 시점의 메모리 상태를 그래프로 모델링하여 진행되며, 이 그래프에서 루트 객체로부터 시작해 참조를 따라갈 수 있는 모든 객체를 '도달 가능'한 것으로 판단한다.
도달 가능성의 기준이 되는 루트 객체는 일반적으로 전역 변수, 현재 실행 중인 함수의 지역 변수 및 매개변수 (즉, 호출 스택에 있는 데이터), 그리고 정적 변수 등이 포함된다. 이 루트들은 프로그램이 명시적으로 접근할 수 있는 객체들의 시작점 역할을 한다. 루트로부터 직접, 또는 다른 도달 가능한 객체를 통해 간접적으로 참조될 수 있는 모든 객체는 사용 중인 것으로 간주되어 보호된다.
이 분석의 반대 개념이 가비지의 정의가 된다. 즉, 루트 집합으로부터 어떤 경로로도 접근할 수 없는 객체는 프로그램에서 더 이상 사용되지 않는 것으로 판단되어 가비지로 식별된다. 이러한 도달 불가능한 객체들이 차지하고 있는 힙 메모리 영역은 이후 단계에서 회수되어 새로운 객체 할당에 재사용될 수 있다. 따라서 이 분석은 메모리 누수를 방지하는 데 있어 가장 근본적인 판단 기준을 제공한다.
도달 가능성 분석은 이론적으로 간단하지만, 실제 런타임 시스템에서는 효율적으로 구현하기 위해 다양한 기법이 적용된다. 예를 들어, 순환 참조 구조가 존재하더라도 루트로부터 도달할 수 없다면 해당 객체 그룹 전체가 가비지로 올바르게 식별될 수 있다. 이 분석의 정확성은 전체 가비지 컬렉션 시스템의 신뢰성의 기초가 된다.
2.2. 마킹(Marking)
2.2. 마킹(Marking)
마킹 단계는 추적 가비지 컬렉션 과정의 첫 번째 핵심 단계로, 메모리 상에서 여전히 사용 중인 모든 객체를 식별하는 작업이다. 이 과정은 루트 셋으로부터 시작된다. 루트 셋은 일반적으로 전역 변수와 현재 실행 중인 모든 스레드의 스택에 있는 지역 변수, 레지스터에 저장된 참조 등을 포함한다. 가비지 컬렉터는 이 루트 셋에 직접 연결된 객체들을 '도달 가능'한 것으로 표시한 후, 이 객체들이 참조하는 다른 객체들로 탐색을 확장해 나간다. 이는 그래프 탐색 알고리즘, 특히 깊이 우선 탐색이나 너비 우선 탐색과 유사한 방식으로 수행되어 모든 도달 가능한 객체의 연결 구조를 순회한다.
마킹을 수행하는 구체적인 방법은 여러 가지가 있다. 가장 기본적인 방식은 각 객체에 별도의 마크 비트를 두어, 해당 객체가 순회 과정에서 발견되면 이 비트를 설정하는 것이다. 일부 구현에서는 객체 헤더의 특정 비트를 재활용하거나, 별도의 외부 비트맵을 사용하기도 한다. 마킹 과정의 정확성을 보장하기 위해서는 트라이-컬러 마킹과 같은 추상화 모델이 사용된다. 이 모델에서는 모든 객체를 흰색(미방문), 회색(방문 중), 검은색(방문 완료) 중 하나의 색상으로 분류하여, 객체 간의 참조 관계가 실시간으로 변경되는 동적인 환경에서도 안전하게 도달 가능성을 추적할 수 있다.
마킹 단계가 완료되면, 메모리 내의 모든 객체는 '도달 가능(마크됨)' 또는 '도달 불가능(마크되지 않음)'으로 명확히 구분된다. 마크되지 않은 객체들은 프로그램의 어떠한 실행 경로로도 접근할 수 없는 가비지로 판단되며, 이후에 수행되는 스위핑 단계에서 메모리 회수의 대상이 된다. 따라서 마킹의 정확성과 완전성은 가비지 컬렉션이 유용한 객체를 실수로 삭제하지 않는 가장 기본적인 보장이 된다.
2.3. 스위핑(Sweeping)
2.3. 스위핑(Sweeping)
스위핑은 마크-앤-스윕 알고리즘의 두 번째 핵심 단계로, 마킹 단계에서 표시되지 않은 모든 객체를 가비지로 간주하여 그들이 차지하고 있던 메모리 공간을 회수하는 과정이다. 이 단계는 힙 전체를 순회하며, 각 메모리 블록의 마크 비트를 확인하여 사용 중인 객체와 사용되지 않는 객체를 구분한다. 사용되지 않는 객체가 차지한 공간은 프리 리스트나 비트맵과 같은 자료 구조에 다시 등록되어 향후 새로운 객체 할당 요청이 들어왔을 때 재사용될 수 있도록 준비된다.
스위핑의 구체적인 구현 방식은 메모리 할당자의 설계에 따라 달라진다. 가장 간단한 형태는 힙을 선형으로 스캔하면서 연속된 가비지 블록들을 하나의 큰 자유 공간으로 병합하는 방식이다. 이는 메모리 단편화를 완화하는 데 도움이 된다. 다른 방식으로는 각 가비지 객체를 개별적으로 프리 리스트에 추가하는 방법이 있으며, 이는 처리 속도는 빠를 수 있지만 단편화가 심해질 수 있다. 스위핑 단계는 일반적으로 애플리케이션의 실행을 중단시키는 스톱-더-월드 현상의 주요 원인 중 하나로, 전체 힙을 검사해야 하기 때문이다.
스위핑 이후, 회수된 메모리는 새로운 객체를 저장하는 데 즉시 사용될 수 있다. 그러나 스위핑 단계 자체는 메모리를 실제로 해제할 뿐, 살아남은 객체들을 메모리 압축하여 단편화를 제거하는 작업은 포함하지 않는다. 따라서 전통적인 마크-앤-스윕은 메모리 단편화를 유발할 수 있으며, 이를 해결하기 위해 별도의 컴팩션 단계를 추가하는 가비지 컬렉터도 존재한다. 스위핑의 효율성은 전체 가비지 컬렉션 사이클의 성능에 직접적인 영향을 미치는 중요한 요소이다.
3. 주요 알고리즘
3. 주요 알고리즘
3.1. 마크-앤-스윕
3.1. 마크-앤-스윕
마크-앤-스윕은 추적 가비지 컬렉션의 가장 기본적이고 대표적인 알고리즘이다. 이 방식은 이름 그대로 두 단계, 즉 마크(Mark) 단계와 스윕(Sweep) 단계로 구성된다. 첫 번째 단계에서는 루트 셋으로부터 시작하여 참조를 따라가며 도달 가능한 모든 객체에 표시(마크)를 한다. 이 과정은 도달 가능성 분석을 수행하여 사용 중인 메모리 영역을 식별하는 것이다.
마크 단계가 완료되면, 표시되지 않은 객체들은 프로그램이 더 이상 접근할 수 없는 가비지로 판단된다. 이후 스윕 단계에서는 힙 전체를 순회하며 마크되지 않은 객체들이 차지하고 있던 메모리를 해제하여 자유 공간으로 만든다. 해제된 공간은 이후 새로운 객체 할당을 위해 재사용될 수 있다.
이 알고리즘의 주요 특징 중 하나는 단편화 문제를 유발할 수 있다는 점이다. 메모리를 연속적으로 해제하기 때문에 시간이 지남에 따라 사용 중인 메모리 블록과 자유 메모리 블록이 교차하게 되어 메모리 단편화가 발생한다. 이로 인해 총 자유 메모리 공간은 충분함에도 불구하고, 연속된 큰 메모리 블록을 할당하지 못하는 상황이 생길 수 있다.
마크-앤-스윕은 구현이 비교적 간단하고 개념이 명확하여 많은 초기 가비지 컬렉터의 기본 모델이 되었다. 그러나 이 방식은 일반적으로 스톱-더-월드 현상을 동반한다. 즉, 가비지 컬렉션을 실행하는 동안에는 애플리케이션의 모든 스레드가 일시 정지되어 프로그램 실행에 지연을 초래한다. 이러한 단점을 보완하기 위해 병행 가비지 컬렉션이나 증분 가비지 컬렉션 같은 다양한 최적화 기법이 개발되었다.
3.2. 트라이-컬러 마킹
3.2. 트라이-컬러 마킹
트라이-컬러 마킹은 마크-앤-스윕 알고리즘을 기반으로 하면서도, 가비지 컬렉션 과정이 프로그램의 실행과 동시에 진행될 수 있도록 설계된 알고리즘이다. 이 기법은 병행 가비지 컬렉션이나 증분 가비지 컬렉션을 구현하는 데 핵심적으로 사용된다. 기존의 단순한 마크-앤-스윕은 스톱-더-월드 현상으로 인해 프로그램 실행이 일시 중단되는 문제가 있었으나, 트라이-컬러 마킹은 이를 완화한다.
이 알고리즘은 모든 객체를 세 가지 색상(흰색, 회색, 검은색) 중 하나로 추상적으로 구분하여 상태를 관리한다. 초기 상태에서 모든 객체는 흰색으로 표시되며, 이는 아직 도달 가능성 분석을 수행하지 않았음을 의미한다. 가비지 컬렉션 사이클이 시작되면, 루트 객체로부터 직접 참조되는 객체들은 회색으로 표시되고 특별한 회색 객체 집합에 넣는다. 이후 회색 집합에서 객체를 하나씩 꺼내어, 그 객체가 참조하는 모든 흰색 객체를 회색으로 바꾸고 집합에 추가하는 과정을 반복한다. 더 이상 회색 객체가 없을 때, 자신을 참조하는 모든 객체를 검사한 객체는 검은색으로 표시된다. 최종적으로 흰색으로 남아 있는 객체들은 어떤 루트 객체로부터도 도달할 수 없는 가비지로 판단되어 메모리에서 회수된다.
트라이-컬러 마킹의 핵심 장점은 마킹 과정 중에도 애플리케이션 스레드가 객체 그래프를 수정할 수 있다는 점이다. 애플리케이션이 검은색 객체가 새로 흰색 객체를 참조하도록 연결하면, 이 참조는 추적되지 않아 가비지로 잘못 회수될 위험이 있다. 이를 방지하기 위해 쓰기 장벽이라는 메커니즘이 사용된다. 쓰기 장벽은 이러한 변경 사항을 감지하여, 새로 참조된 흰색 객체를 즉시 회색으로 표시함으로써 마킹 과정에 포함시킨다. 이를 통해 마킹의 정확성을 유지하면서도 컬렉션과 프로그램 실행을 동시에 진행할 수 있다.
이 알고리즘은 JVM의 CMS 컬렉터나 G1 가비지 컬렉터, 그리고 .NET의 가비지 컬렉터와 같은 현대적인 메모리 관리 시스템에서 광범위하게 채택되고 있다. 프로그램의 중단 시간을 최소화해야 하는 실시간 시스템이나 대화형 애플리케이션에서 특히 유용하게 적용된다.
3.3. 스톱-더-월드
3.3. 스톱-더-월드
스톱-더-월드(Stop-the-World)는 가비지 컬렉션이 수행되는 동안 애플리케이션의 모든 실행 스레드를 일시적으로 중단시키는 현상 또는 방식을 가리킨다. 이는 마크-앤-스윕과 같은 추적 방식의 가비지 컬렉터가 메모리 상태를 안정적으로 분석하기 위해 필요한 과정이다. 가비지 컬렉터가 도달 가능성 분석을 수행할 때, 애플리케이션이 객체 그래프를 계속 변경하면 분석 결과가 일관되지 않을 수 있다. 따라서 분석이 완료될 때까지 모든 애플리케이션 스레드를 멈춤으로써 객체 간 참조 관계가 고정된 상태를 확보한다.
스톱-더-월드가 발생하는 시간, 즉 가비지 컬렉션 일시 정지 시간은 처리해야 할 라이브 객체(Live Object)의 수와 힙(Heap)의 전체 크기에 따라 달라진다. 이 시간이 길어지면 애플리케이션의 응답 시간이 저하되거나 서비스가 중단되는 것처럼 보일 수 있어, 실시간 시스템이나 대화형 애플리케이션에서는 중요한 성능 문제로 간주된다. 이러한 단점을 완화하기 위해 병행 가비지 컬렉션이나 증분 가비지 컬렉션과 같은 최적화 기법이 개발되었다.
4. 최적화 기법
4. 최적화 기법
4.1. 세대별 가비지 컬렉션
4.1. 세대별 가비지 컬렉션
세대별 가비지 컬렉션은 객체의 생존 기간에 대한 경험적 관찰, 즉 "대부분의 객체는 생성된 지 얼마 되지 않아 죽는다"는 약한 세대 가설에 기반한 최적화 기법이다. 이 기법은 메모리 공간을 여러 세대로 나누어 관리하며, 새로 생성된 객체는 주로 젊은 세대에 할당된다. 젊은 세대에서의 가비지 컬렉션은 매우 빈번하게 발생하지만, 해당 영역의 크기가 작기 때문에 처리 속도가 빠르다. 반면, 여러 번의 가비지 컬렉션 주기를 거쳐 살아남은 객체는 오래된 세대로 승격되어 관리되며, 이 영역에서는 가비지 컬렉션이 상대적으로 덜 자주 수행된다.
이 방식의 핵심은 세대 간의 참조를 효율적으로 처리하는 것이다. 젊은 세대의 객체는 오래된 세대의 객체를 참조할 수 있지만, 그 반대 방향의 참조는 일반적으로 드물게 발생한다고 가정한다. 이러한 가정을 바탕으로 카드 테이블이나 리멤버드 세트 같은 기법을 사용해 세대 간의 참조를 기록함으로써, 전체 힙을 스캔하지 않고도 특정 세대만을 대상으로 한 가비지 컬렉션을 가능하게 한다. 이는 불필요한 메모리 검사 범위를 크게 줄여준다.
세대별 가비지 컬렉션의 대표적인 구현 형태는 젊은 세대, 오래된 세대, 그리고 때로는 영구 세대 또는 메타스페이스로 힙을 구분하는 것이다. 젊은 세대 내부는 다시 에덴 공간과 두 개의 생존자 공간으로 나뉘어 카피링 가비지 컬렉션 알고리즘이 적용되는 경우가 많다. 이 구조 하에서 객체는 에덴 공간에 생성되고, 첫 번째 마이너 가비지 컬렉션에서 살아남으면 생존자 공간으로 복사된다. 일정 횟수 이상 생존자 공간을 왕복하며 살아남은 객체는 결국 오래된 세대로 이동하게 된다.
이 최적화 기법은 자바 가상 머신의 대부분의 가비지 컬렉터와 .NET의 공용 언어 런타임 가비지 컬렉터에서 채택하고 있는 핵심 메모리 관리 전략이다. 이를 통해 애플리케이션의 전체적인 스루풋을 높이면서도 가비지 컬렉션으로 인한 정지 시간을 현실적으로 관리할 수 있게 되었다.
4.2. 병행 가비지 컬렉션
4.2. 병행 가비지 컬렉션
병행 가비지 컬렉션은 애플리케이션의 사용자 스레드가 실행을 멈추지 않고, 별도의 스레드에서 가비지 컬렉션 작업을 동시에 수행하는 기법이다. 이 방식은 스톱-더-월드 현상을 최소화하여 애플리케이션의 응답 시간을 크게 개선하는 것을 목표로 한다. 가비지 컬렉터 스레드는 주로 도달 가능성 분석을 위한 마킹 단계를 백그라운드에서 수행하며, 애플리케이션 스레드는 이 과정에서도 새로운 객체를 할당하고 기존 객체를 참조할 수 있다.
이 기법의 핵심 과제는 애플리케이션 스레드가 가비지 컬렉터 스레드의 작업을 방해하거나, 그 반대의 상황이 발생하지 않도록 동기화하는 것이다. 예를 들어, 가비지 컬렉터가 특정 객체를 아직 살아있다고 표시하는 동안 애플리케이션 스레드가 그 객체에 대한 모든 참조를 제거할 수 있다. 이를 방지하기 위해 트라이-컬러 마킹 알고리즘이 널리 사용되며, 객체의 상태를 흰색, 회색, 검은색으로 구분하여 동시 수정을 안전하게 관리한다.
병행 가비지 컬렉션은 특히 대화형 애플리케이션이나 실시간 시스템과 같이 짧은 지연 시간이 중요한 환경에서 유용하다. JVM의 가비지 컬렉터 중 하나인 CMS 가비지 컬렉터나 G1 가비지 컬렉터는 이러한 병행 처리를 구현한 대표적인 예시이다. 그러나 완전한 스톱-더-월드를 피할 수는 있어도, 메모리 할당이나 최종 정리 단계에서 짧은 정지는 여전히 발생할 수 있으며, 구현 복잡도가 높고 추가적인 CPU 자원을 소모한다는 단점도 있다.
4.3. 증분 가비지 컬렉션
4.3. 증분 가비지 컬렉션
증분 가비지 컬렉션은 가비지 컬렉션 작업을 여러 개의 작은 단위로 나누어 수행하는 기법이다. 이 방식은 한 번에 모든 가비지를 수집하는 대신, 컬렉션 작업을 프로그램 실행 중에 짧은 간격으로 분산시켜 처리한다. 이를 통해 스톱-더-월드 현상으로 인한 긴 지연 시간을 줄이고, 애플리케이션의 응답성을 향상시키는 데 목적이 있다. JVM이나 .NET 런타임 시스템과 같은 환경에서 사용자 경험을 개선하기 위해 중요한 최적화 기법으로 활용된다.
증분 가비지 컬렉션의 핵심은 마킹 단계를 점진적으로 수행하는 것이다. 루트 셋으로부터 시작하는 도달 가능성 분석을 한 번에 완료하지 않고, 각 컬렉션 사이클에서 일부 객체만을 대상으로 마킹과 스위핑을 진행한다. 이 과정에서 프로그램 실행과 컬렉션 작업이 번갈아가며 이루어지기 때문에, 뮤테이터에 의해 객체 그래프가 변경되는 상황을 고려해야 한다. 이를 위해 트라이-컬러 마킹과 같은 알고리즘이 증분 수집을 지원하는 데 자주 사용된다.
이 기법의 주요 장점은 가비지 컬렉션으로 인한 일시 정지 시간을 현저히 단축할 수 있다는 점이다. 대규모 힙 메모리를 관리할 때 특히 효과적이며, 실시간 시스템이나 대화형 애플리케이션에 적합하다. 그러나 단점으로는 전체적인 컬렉션 오버헤드가 증가할 수 있고, 구현이 복잡해진다는 점이 있다. 프로그램 실행과 수집 작업이 병행되므로 추가적인 동기화 비용이 발생하며, 모든 가비지를 한 번에 수집하는 전통적인 방식에 비해 메모리 회수 효율이 다소 낮을 수 있다.
5. 장단점
5. 장단점
5.1. 장점
5.1. 장점
추적 가비지 컬렉션의 가장 큰 장점은 프로그래머가 명시적으로 메모리 할당과 해제를 관리할 필요가 없다는 점이다. 이는 메모리 누수와 댕글링 포인터와 같은 수동 메모리 관리에서 흔히 발생하는 오류를 근본적으로 방지해 준다. 개발자는 동적 메모리 할당에 집중할 수 있고, 복잡한 객체의 생명주기를 추적하는 부담에서 벗어나 애플리케이션의 핵심 로직 개발에 더 많은 시간을 할당할 수 있다.
또한, 이 기법은 안전성을 크게 향상시킨다. 프로그램이 이미 해제된 메모리 영역을 참조하여 발생하는 세그멘테이션 폴트나 예측 불가능한 동작을 방지한다. 이는 특히 대규모 및 장기 실행 소프트웨어의 안정성을 보장하는 데 결정적인 역할을 한다. 자바, C 샤프, 파이썬, 자바스크립트와 같은 현대의 주요 프로그래밍 언어들이 이 방식을 채택한 이유도 여기에 있다.
마지막으로, 메모리 회수 과정이 체계적이기 때문에 힙 단편화를 완화하는 데 도움이 될 수 있다. 스위핑 단계 이후 또는 압축을 동반하는 알고리즘을 통해 사용 중인 메모리 블록들을 재배치하여 연속된 여유 공간을 확보할 수 있다. 이는 장기적으로 메모리 할당의 효율성을 유지하고 전체적인 시스템 성능에 긍정적인 영향을 미친다.
5.2. 단점
5.2. 단점
추적 가비지 컬렉션은 자동 메모리 관리를 제공하지만, 몇 가지 고유한 단점을 가지고 있다. 가장 큰 문제는 스톱-더-월드 현상으로, 가비지 컬렉션이 수행되는 동안 애플리케이션의 모든 실행 스레드가 일시 중단된다는 점이다. 이로 인해 응답 시간이 길어지거나 지연이 발생할 수 있으며, 실시간 시스템이나 고성능이 요구되는 애플리케이션에서는 심각한 문제가 될 수 있다. 또한, 가비지 컬렉션을 수행하는 데 필요한 추가적인 CPU 사이클과 메모리 오버헤드가 발생하여 전체적인 시스템 성능에 영향을 미친다.
두 번째로, 메모리 사용 효율성이 상대적으로 낮을 수 있다. 가비지 컬렉터가 작동하기 위해서는 일반적으로 사용 가능한 총 메모리 양보다 더 많은 힙 메모리를 필요로 한다. 또한, 메모리 단편화 문제가 발생할 수 있는데, 특히 마크-앤-스윕 알고리즘의 경우 객체를 회수한 후에도 사용 중인 메모리 블록 사이에 빈 공간이 산발적으로 생겨, 총 여유 메모리는 충분함에도 불구하고 큰 객체를 할당하지 못하는 상황이 발생할 수 있다. 이는 메모리 압축 과정을 통해 해결되지만, 이 과정 자체도 추가적인 오버헤드를 유발한다.
마지막으로, 프로그래머의 통제 범위가 제한된다는 점이다. 가비지 컬렉션은 자동으로 수행되므로 객체가 정확히 언제 메모리에서 해제될지 예측하기 어렵다. 이는 파일 핸들이나 네트워크 소켓과 같은 비메모리 자원의 해제 시점과 맞추기 어렵게 만들어, 자원 누수의 위험을 초래할 수 있다. 또한, 메모리 해제 패턴이 불규칙하여 캐시 지역성을 저해하고 성능을 떨어뜨릴 수 있다.
6. 사용 예시
6. 사용 예시
6.1. JVM의 가비지 컬렉터
6.1. JVM의 가비지 컬렉터
자바 가상 머신(JVM)은 추적 가비지 컬렉션을 핵심 메모리 관리 메커니즘으로 채택하고 있다. JVM의 가비지 컬렉터(GC)는 힙 메모리 영역을 관리하며, 개발자가 명시적으로 메모리를 해제할 필요 없이 더 이상 사용되지 않는 객체를 자동으로 회수한다. 이를 통해 자바 프로그래머는 메모리 관리의 부담을 크게 줄이고 메모리 누수 방지에 집중할 수 있다.
JVM의 가비지 컬렉션은 기본적으로 루트 셋으로부터 시작하는 도달 가능성 분석을 기반으로 한다. 스택의 지역 변수나 메서드 영역의 정적 변수 등 루트에서 시작해 참조를 따라갈 수 있는 모든 객체는 '살아있는' 객체로 표시(Mark)된다. 이 과정을 거친 후, 표시되지 않은 객체는 프로그램에서 더 이상 접근할 수 없는 가비지로 판단되어 메모리에서 해제(Sweep)된다.
JVM은 다양한 가비지 컬렉션 알고리즘을 구현하여 성능과 응답 시간 요구사항에 맞춰 선택할 수 있도록 한다. 대표적인 예로 세대별 가비지 컬렉션을 사용하는 병렬 수집기(Parallel GC)와 가비지 퍼스트 수집기(G1 GC), 그리고 매우 짧은 정지 시간을 목표로 하는 저지연 수집기(ZGC)와 쉐난도 수집기(Shenandoah GC) 등이 있다. 각 수집기는 스톱-더-월드 현상의 지속 시간, 처리량, 메모리 단편화 처리 방식 등에서 차이를 보인다.
가비지 컬렉터 | 주요 특징 | 적합한 워크로드 |
|---|---|---|
Serial GC | 단일 스레드로 동작하는 마크-앤-스윕 수집기. | 클라이언트 애플리케이션, 작은 힙. |
Parallel GC (Throughput GC) | 다중 스레드를 사용해 처리량을 극대화. | 배치 처리, 높은 처리량이 중요한 서버. |
CMS GC (Concurrent Mark-Sweep) | 대부분의 작업을 애플리케이션 스레드와 병행 수행하여 정지 시간을 줄임. | 낮은 지연 시간이 중요한 애플리케이션. |
G1 GC (Garbage-First) | 힙을 영역(Region)으로 나누고, 가비지가 많은 영역을 우선적으로 회수. | 큰 힙과 예측 가능한 정지 시간이 필요한 경우. |
ZGC / Shenandoah GC | 매우 큰 힙에서도 10ms 미만의 매우 짧은 정지 시간을 목표로 하는 최신 저지연 수집기. | 대용량 메모리와 극단적으로 낮은 지연 시간이 요구되는 서비스. |
이러한 다양한 옵션은 JVM이 서버, 모바일 장치, 임베디드 시스템 등 광범위한 환경에서 효율적으로 실행될 수 있는 기반을 제공한다.
6.2. .NET의 가비지 컬렉터
6.2. .NET의 가비지 컬렉터
.NET 프레임워크의 가비지 컬렉터는 공용 언어 런타임(CLR)의 핵심 구성 요소로, 관리 코드가 실행되는 환경에서 자동 메모리 관리를 제공한다. 이는 C#이나 Visual Basic .NET과 같은 언어로 작성된 응용 프로그램이 명시적으로 메모리를 해제하지 않아도, 더 이상 사용되지 않는 객체를 자동으로 식별하고 회수하여 메모리 누수를 방지한다. .NET의 가비지 컬렉션 구현은 성능과 효율성을 극대화하기 위해 여러 최적화 기법을 통합하고 있다.
가비지 컬렉션의 핵심은 도달 가능성 분석을 기반으로 한다. 루트라고 불리는 활성 참조(전역 변수, 지역 변수, 정적 필드 등)로부터 시작하여 그래프 탐색을 수행해 접근 가능한 모든 객체를 표시(Mark)한다. 이 과정에서 도달할 수 없는 객체는 가비지로 간주된다. 이후 스위핑(Sweeping) 단계에서 이러한 가비지 객체가 차지하고 있던 메모리 공간을 회수하여 이후의 메모리 할당에 재사용할 수 있게 한다.
.NET 가비지 컬렉터의 가장 두드러진 특징은 세대별 가비지 컬렉션(Generational GC)을 채택한 것이다. 이는 객체를 수명에 따라 0세대(가장 젊은 객체), 1세대, 2세대(가장 오래된 객체)로 나누어 관리한다. 대부분의 객체는 생성 후 짧은 시간 내에 더 이상 사용되지 않는 경향이 있으므로, 가비지 컬렉터는 비용이 적게 드는 0세대 힙 영역을 더 자주 수집함으로써 전체적인 성능을 향상시킨다. 오래 생존한 객체는 상대적으로 덜 자주 수집되는 2세대로 승격된다.
또한, .NET의 가비지 컬렉터는 병행 가비지 컬렉션과 백그라운드 가비지 컬렉션을 지원하여 애플리케이션의 실행을 완전히 중단(스톱-더-월드)시키는 시간을 최소화하려고 노력한다. 이를 통해 대화형 애플리케이션이나 서버 애플리케이션에서 발생할 수 있는 응답 지연을 줄인다. 관리되는 메모리 모델과 함께 작동하는 이 가비지 컬렉션 시스템은 .NET 기반 소프트웨어의 개발 생산성과 안정성을 높이는 데 기여한다.
